package com.jeasonzhao.commons.json;

import com.jeasonzhao.commons.parser.lex.LexException;
import com.jeasonzhao.commons.parser.lex.LexToken;
import com.jeasonzhao.commons.parser.lex.LexTokenCollection;
import com.jeasonzhao.commons.parser.lex.LexTokenTypes;
import com.jeasonzhao.commons.utils.Algorithms;

public class JSONParser
{
    public static final String SPEC_ARRAY_DELIMETER = ",";
    public static final String SPEC_CLASS_DELIMETER = ",";
    public static final String SPEC_ASSIGN_DELIMETER = ":";
    public static final String ARRAY_BEGIN = "[";
    public static final String ARRAY_END = "]";
    public static final String CLASS_BEGIN = "{";
    public static final String CLASS_END = "}";

    private int m_nIndexId = 0;
    private LexTokenCollection m_tokens = null;
    public JSONParser(String str)
        throws LexException
    {
        m_tokens = (new JSONTokenizer(str)).getTokens(false,false);
    }

    public JSONParser(LexTokenCollection tks)
    {
        if(null == tks)
        {
            this.m_tokens = null;
        }
        else
        {
            m_tokens = new LexTokenCollection();
            for(LexToken t : tks)
            {
                m_tokens.add(t);
            }
        }
    }

    public static JSONNode parse(String str)
        throws LexException,JSONException
    {
        JSONParser p = new JSONParser(str);
        return p.next();
    }

    public JSONNode next()
        throws JSONException,LexException
    {
        if(null == m_tokens
           || m_nIndexId >= m_tokens.size())
        {
            return null;
        }
        LexToken tk = m_tokens.get(m_nIndexId);
        LexToken tkNext = m_nIndexId + 1 < m_tokens.size() ? m_tokens.get(m_nIndexId + 1) : null;
//        System.out.println(tk.getToken()+">>>"+tk.getType());
        if(tk.getType() == LexTokenTypes.SPECIAL
           && tk.getToken().equals(ARRAY_BEGIN))
        {
            return parseArray();
        }
        else if(tk.getType() == LexTokenTypes.SPECIAL
                && tk.getToken().equals(CLASS_BEGIN))
        {
            return parseClass();
        }
        else if((tk.isName() || tk.isString())
                && tkNext != null
                && tkNext.equals(SPEC_ASSIGN_DELIMETER))
        {
            return parseAssign();
        }
        else if(tk.getType() != LexTokenTypes.SPECIAL)
        {
            m_nIndexId++;
            return new JSONConstant(tk);
        }
        else
        {
            m_nIndexId++;
            return new JSONSpecial(tk);
        }
    }

    private JSONAssign parseAssign()
        throws LexException,JSONException
    {
        LexTokenCollection assignNodes = new LexTokenCollection();
        LexToken nameTokenoken = m_tokens.get(m_nIndexId);
        assignNodes.add(nameTokenoken);
        m_nIndexId++;
        assignNodes.add(m_tokens.get(m_nIndexId));
        m_nIndexId++;
        JSONParser p = new JSONParser(m_tokens.slice(m_nIndexId, -1));
        JSONNode valueNode = p.next();

        m_nIndexId += valueNode.amountOfTokens();
        assignNodes.addAll(valueNode.m_tokens);
        String strVariableName = nameTokenoken.getToken();
        if(Algorithms.isValidateSymbolName(strVariableName) == false)
        {
            throw new JSONException(nameTokenoken,"The token \"" + strVariableName + "\" CAN NOT be a variable name");
        }
        return new JSONAssign(assignNodes,strVariableName,valueNode);
    }

    public String toString()
    {
        return null == m_tokens ? "" : m_tokens.toString();
    }

    private JSONClass parseClass()
        throws JSONException,LexException
    {
        int nDeep = 0;
        LexTokenCollection nodes = new LexTokenCollection();
        LexToken tkFirst = m_tokens.get(m_nIndexId);
        while(m_nIndexId < m_tokens.size())
        {
            LexToken tk = m_tokens.get(m_nIndexId);
            m_nIndexId++;
            if(tk.getType() == LexTokenTypes.SPECIAL && tk.getToken().equals(CLASS_BEGIN))
            {
                nDeep++;
                nodes.add(tk);
            }
            else if(tk.getType() == LexTokenTypes.SPECIAL && tk.getToken().equals(CLASS_END))
            {
                nDeep--;
                nodes.add(tk);
                if(nDeep == 0)
                {
                    break;
                }
            }
            else
            {
                nodes.add(tk);
            }
        }
        if(nDeep > 0)
        {
            throw new JSONException(tkFirst,"Missing symbol \"" + CLASS_END + "\"");
        }
        JSONClass cls = new JSONClass(nodes);
        return cls;
    }

    private JSONArray parseArray()
        throws JSONException,LexException
    {
        int nDeep = 0;
        LexTokenCollection nodes = new LexTokenCollection();
        LexToken tkFirst = m_tokens.get(m_nIndexId);
        while(m_nIndexId < m_tokens.size())
        {
            LexToken tk = m_tokens.get(m_nIndexId);
            m_nIndexId++;
            if(tk.getType() == LexTokenTypes.SPECIAL && tk.getToken().equals(ARRAY_BEGIN))
            {
                nDeep++;
                nodes.add(tk);
            }
            else if(tk.getType() == LexTokenTypes.SPECIAL && tk.getToken().equals(ARRAY_END))
            {
                nDeep--;
                nodes.add(tk);
                if(nDeep == 0)
                {
                    break;
                }
            }
            else
            {
                nodes.add(tk);
            }
        }
        if(nDeep > 0)
        {
            throw new JSONException(tkFirst,"un-matched brackets");
        }
        JSONArray ary = new JSONArray(nodes);
//        System.out.println("ARRAY:"+nodes.toString()+">>>"+nodes.size()+">>>>"+ary.size());
        return ary;
    }

//    public static void main(String argv[])
//        throws LexException
//    {
//
//    }
}
